﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Elderos.Geometry
{
	public class PointCollection : IList<Point>
	{
		private readonly List<Point> _points = new List<Point>();

		public int? Dimensions { get; private set; }

		public PointCollection()
		{}

		public PointCollection(IEnumerable<Point> points)
			: this()
		{
			foreach (var point in points)
			{
				Add(point);
			}
		}

		public Point Centroid
		{
			get
			{
				if (!Dimensions.HasValue || _points.Count <= 0)
                    throw new InvalidOperationException("Can't get centroid of an empty PointCollection.");

				var dimensionsAvg = new double[Dimensions.Value];

				foreach (var point in _points)
				{
					for (int i = 0; i < point.Dimensions; i++)
					{
						dimensionsAvg[i] += point.Coordinates[i]/_points.Count;
					}
				}

				Point avg = new Point(dimensionsAvg);
				return avg;
			}
		}


		#region Overrides of IList<Point>
		/// <summary>
		/// Добавляет элемент к списку.
		/// </summary>
		/// <param name="point"></param>
		public void Add(Point point)
		{
			if (!Dimensions.HasValue)
				Dimensions = point.Dimensions;

			_points.Add(point);
		}

		/// <summary>
		/// Удаляет все элементы из интерфейса <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">Объект <see cref="T:System.Collections.Generic.ICollection`1"/> доступен только для чтения. </exception>
		public void Clear()
		{
			Dimensions = null;
			_points.Clear();
		}

		/// <summary>
		/// Определяет, содержит ли интерфейс <see cref="T:System.Collections.Generic.ICollection`1"/> указанное значение.
		/// </summary>
		/// <returns>
		/// Значение true, если объект <paramref name="item"/> найден в <see cref="T:System.Collections.Generic.ICollection`1"/>; в противном случае — значение false.
		/// </returns>
		/// <param name="item">Объект, который требуется найти в <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
		public bool Contains(Point item)
		{
			return _points.Contains(item);
		}

		/// <summary>
		/// Копирует элементы <see cref="T:System.Collections.Generic.ICollection`1"/> в массив <see cref="T:System.Array"/>, начиная с указанного индекса <see cref="T:System.Array"/>.
		/// </summary>
		/// <param name="array">Одномерный массив <see cref="T:System.Array"/>, в который копируются элементы из интерфейса <see cref="T:System.Collections.Generic.ICollection`1"/>.Индексация в массиве <see cref="T:System.Array"/> должна начинаться с нуля.</param><param name="arrayIndex">Отсчитываемый от нуля индекс в массиве <paramref name="array"/>, с которого начинается копирование.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> имеет значение null;</exception><exception cref="T:System.ArgumentOutOfRangeException">Значение параметра <paramref name="arrayIndex"/> меньше 0.</exception><exception cref="T:System.ArgumentException">Параметр <paramref name="array"/> является многомерным— или —Количество элементов в исходной коллекции <see cref="T:System.Collections.Generic.ICollection`1"/> превышает доступное место, начиная с индекса <paramref name="arrayIndex"/> до конца массива назначения <paramref name="array"/>.</exception>
		public void CopyTo(Point[] array, int arrayIndex)
		{
			_points.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Удаляет первое вхождение указанного объекта из интерфейса <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// Значение true, если объект <paramref name="item"/> успешно удален из <see cref="T:System.Collections.Generic.ICollection`1"/>, в противном случае — значение false.Этот метод также возвращает значение false, если параметр <paramref name="item"/> не найден в исходном интерфейсе <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		/// <param name="item">Объект, который необходимо удалить из интерфейса <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">Объект <see cref="T:System.Collections.Generic.ICollection`1"/> доступен только для чтения.</exception>
		public bool Remove(Point item)
		{
			return _points.Remove(item);
		}

		/// <summary>
		/// Получает число элементов, содержащихся в интерфейсе <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <returns>
		/// Число элементов, содержащихся в интерфейсе <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		public int Count
		{
			get { return _points.Count; }
		}

		/// <summary>
		/// Получает значение, указывающее, доступен ли интерфейс <see cref="T:System.Collections.Generic.ICollection`1"/> только для чтения.
		/// </summary>
		/// <returns>
		/// Значение true, если интерфейс <see cref="T:System.Collections.Generic.ICollection`1"/> доступен только для чтения, в противном случае — значение false.
		/// </returns>
		public bool IsReadOnly
		{
			get { return false; }
		}

		/// <summary>
		/// Возвращает перечислитель, выполняющий итерацию в коллекции.
		/// </summary>
		/// <returns>
		/// Интерфейс <see cref="T:System.Collections.Generic.IEnumerator`1"/>, который может использоваться для перебора элементов коллекции.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<Point> GetEnumerator()
		{
			return _points.GetEnumerator();
		}

		/// <summary>
		/// Возвращает перечислитель, который осуществляет перебор элементов коллекции.
		/// </summary>
		/// <returns>
		/// Объект <see cref="T:System.Collections.IEnumerator"/>, который может использоваться для перебора элементов коллекции.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		/// <summary>
		/// Определяет индекс заданного элемента коллекции <see cref="T:System.Collections.Generic.IList`1"/>.
		/// </summary>
		/// <returns>
		/// Индекс <paramref name="item"/> если он найден в списке; в противном случае его значение равно -1.
		/// </returns>
		/// <param name="item">Объект, который требуется найти в <see cref="T:System.Collections.Generic.IList`1"/>.</param>
		public int IndexOf(Point item)
		{
			return _points.IndexOf(item);
		}

		/// <summary>
		/// Вставляет элемент в список <see cref="T:System.Collections.Generic.IList`1"/> по указанному индексу.
		/// </summary>
		/// <param name="index">Индекс (с нуля), по которому следует вставить параметр <paramref name="item"/>.</param><param name="item">Объект, вставляемый в <see cref="T:System.Collections.Generic.IList`1"/>.</param><exception cref="T:System.ArgumentOutOfRangeException">Значение параметра <paramref name="index"/> не является допустимым индексом в <see cref="T:System.Collections.Generic.IList`1"/>.</exception><exception cref="T:System.NotSupportedException">Объект <see cref="T:System.Collections.Generic.IList`1"/> доступен только для чтения.</exception>
		public void Insert(int index, Point item)
		{
			if (!Dimensions.HasValue)
				Dimensions = item.Dimensions;

			_points.Insert(index, item);
		}

		/// <summary>
		/// Удаляет элемент <see cref="T:System.Collections.Generic.IList`1"/> по указанному индексу.
		/// </summary>
		/// <param name="index">Индекс (с нуля) удаляемого элемента.</param><exception cref="T:System.ArgumentOutOfRangeException">Значение параметра <paramref name="index"/> не является допустимым индексом в <see cref="T:System.Collections.Generic.IList`1"/>.</exception><exception cref="T:System.NotSupportedException">Объект <see cref="T:System.Collections.Generic.IList`1"/> доступен только для чтения.</exception>
		public void RemoveAt(int index)
		{
			_points.RemoveAt(index);
		}

		/// <summary>
		/// Получает или задает элемент по указанному индексу.
		/// </summary>
		/// <returns>
		/// Элемент с указанным индексом.
		/// </returns>
		/// <param name="index">Индекс (с нуля) элемента, который необходимо получить или задать.</param><exception cref="T:System.ArgumentOutOfRangeException">Значение параметра <paramref name="index"/> не является допустимым индексом в <see cref="T:System.Collections.Generic.IList`1"/>.</exception><exception cref="T:System.NotSupportedException">Свойство задано, и объект <see cref="T:System.Collections.Generic.IList`1"/> доступен только для чтения.</exception>
		public Point this[int index]
		{
			get { return _points[index]; }
			set { _points[index] = value; }
		}
		#endregion
	}
}
